﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Web;

namespace Jenvin.Library.Functional
{
    public static class StringExtensions
    {
        /// <summary>
        /// 对字符串执行不区分大小写的比较
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public static bool EqualsIgnoreCase(this string str1, string str2)
        {
            return string.Equals(str1, str2, StringComparison.InvariantCultureIgnoreCase);
        }

        /// <summary>
        /// 返回source中 在start 和 end 之间的字串。如果有重复出现的，返回第一个 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static string Between(this string source, string start, string end)
        {
            return Between(source, start, end, 0);
        }

        /// <summary>
        /// 返回source中 在start 和 end 之间的字串。如果有重复出现的，返回第一个.
        /// index控制从什么位置开始查找
        /// </summary>
        /// <param name="source"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static string Between(this string source, string start, string end, int index)
        {
            if (string.IsNullOrEmpty(start))
                throw new ArgumentException("start");

            if (string.IsNullOrEmpty(end))
                throw new ArgumentException("end");

            if (index >= source.Length || index < 0)
                throw new ArgumentException("index");


            int indexStart = source.IndexOf(start, index);
            if (indexStart < 0) return string.Empty;

            indexStart += start.Length;
            int indexEnd = source.IndexOf(end, indexStart);
            if (indexEnd <= indexStart) return string.Empty;

            return source.Substring(indexStart, indexEnd - indexStart);
        }

        /// <summary>
        /// 返回source中 stub左边的字串.不包含stub
        /// </summary>
        /// <param name="source"></param>
        /// <param name="stub"></param>
        /// <returns></returns>
        public static string Left(this string source, string stub)
        {
            if (stub == null)
                throw new ArgumentNullException("stub");
            if (stub == string.Empty)
                return source;

            int stubIndex = source.IndexOf(stub);
            if (stubIndex < 0)
                return string.Empty;//没有包含，左边 应为空

            return source.Substring(0, stubIndex);
        }

        /// <summary>
        /// 返回source中 stub左边的字串.不包含stub
        /// </summary>
        /// <param name="source"></param>
        /// <param name="stub"></param>
        /// <returns></returns>
        public static string Right(this string source, string stub)
        {
            if (stub == null)
                throw new ArgumentNullException("stub");
            if (stub == string.Empty)
                return source;

            int stubIndex = source.IndexOf(stub);
            if (stubIndex < 0)
                return source;//没有包含，右边应理解为全部

            stubIndex += stub.Length;
            if (stubIndex >= source.Length - 1)
                return string.Empty;

            return source.Substring(stubIndex);
        }

        /// <summary>
        /// 载断字符串
        /// </summary>
        /// <param name="source">源</param>
        /// <param name="n">保留多少个字</param>
        /// <returns></returns>
        public static string Cut(this string source, int n)
        {
            return Cut(source, n, string.Empty);
        }

        /// <summary>
        /// 载断字符串
        /// </summary>
        /// <param name="source">源</param>
        /// <param name="n">保留多少个字</param>
        /// <param name="ending">被截断的字符用什么表示 比如 “…”,注意n已经包含了ending的长度。</param>
        /// <returns></returns>
        public static string Cut(this string source, int n, string ending)
        {
            if (n < 0)
                throw new ArgumentException("n");

            if (source == string.Empty)
                return string.Empty;

            ending = ending ?? "";

            string ret = string.Empty;
            int len = Encoding.Default.GetByteCount(source);
            int endLen = Encoding.Default.GetByteCount(ending);
            if (len <= n)
                return source;
            int t = 0;
            char[] sourceChars = source.ToCharArray();
            for (int i = 0; i < sourceChars.Length && t < n - endLen; i++)
            {
                char c = sourceChars[i];
                ret += c;
                if ((int)c >= 0x4E00 && (int)c <= 0x9FA5)//是否汉字
                    t += 2;
                else
                    t++;
            }
            return ret + ending;
        }
        /// <summary>
        /// 转全角的函数(SBC case)
        /// </summary>
        /// <param name="source">任意字符串</param>
        /// <returns>全角字符串</returns>
        ///<remarks>
        ///全角空格为12288，半角空格为32
        ///其他字符半角(33-126)与全角(65281-65374)的对应关系是：均相差65248
        ///</remarks>
        public static string ToSBC(this string source)
        {
            //半角转全角：
            char[] c = source.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 32)
                {
                    c[i] = (char)12288;
                    continue;
                }
                if (c[i] < 127)
                    c[i] = (char)(c[i] + 65248);
            }
            return new string(c);
        }

        /// <summary> 转半角的函数(DBC case) </summary>
        /// <param name="source">任意字符串</param>
        /// <returns>半角字符串</returns>
        ///<remarks>
        ///全角空格为12288，半角空格为32
        ///其他字符半角(33-126)与全角(65281-65374)的对应关系是：均相差65248
        ///</remarks>
        public static string ToDBC(this string source)
        {
            char[] c = source.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 12288)
                {
                    c[i] = (char)32;
                    continue;
                }
                if (c[i] > 65280 && c[i] < 65375)
                    c[i] = (char)(c[i] - 65248);
            }
            return new string(c);
        }

        private static Regex invalidXmlReg = new Regex(
"[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]", RegexOptions.IgnoreCase);
        /// <summary>
        /// 过滤Xml不允许的Xml标签 。非打打 印字符 。
        /// 同时对"&lt;" ,"&gt;","&quot;","&apos;","&amp;" 编码 
        /// 通过 ToSafeXmlValue 过滤的字串 可以直接 安全的将.string load到XmlDocument
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ToSafeXmlValue(this string text)
        {
            if (text == "")
                return string.Empty;
            // From xml spec valid chars:  
            // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]    #x2  
            // any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.  
            //string re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]";


            // "<"      "&lt;"  
            //">"      "&gt;" 
            //"\""     "&quot;"  
            //"\'"     "&apos;"  
            //"&"      "&amp;" 
            return invalidXmlReg.Replace(text, new MatchEvaluator(x =>
            {

                switch (x.Value)
                {
                    case "<": return "&lt;";
                    case ">": return "&gt;";
                    case "\"": return "&quot;";
                    case "\'": return "&apos;";
                    case "&": return "&amp;";
                    default:
                        return string.Empty;
                }
            }));
        }
        /// <summary>
        /// 过滤Xml不允许的Xml标签 。非打打 印字符 。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ToSafeXml(this string text)
        {
            if (text == "")
                return string.Empty;

            return invalidXmlReg.Replace(text, "");
        }

        /// <summary>
        ///  过滤Xml不允许的Xml标签 。非打打 印字符 。不使用正侧 
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ToValidXml(this string text)
        {
            if (text == "")
                return string.Empty;
            StringBuilder sb = new StringBuilder(text.Length);
            foreach (char c in text)
            {
                if (IsLegalXmlChar(c))
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
        /// <summary>
        /// 过滤Xml不允许的Xml标签 。非打打 印字符 。
        /// 同时对"&lt;" ,"&gt;","&quot;","&apos;","&amp;" 编码 
        /// 通过 ToSafeXmlValue 过滤的字串 可以直接 安全的将.string load到XmlDocument 不使用正侧
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ToValidXmlValue(this string text)
        {
            if (text == "")
                return string.Empty;
            StringBuilder sb = new StringBuilder(text.Length);
            foreach (char c in text)
            {
                if (IsLegalXmlChar(c))
                {
                    switch (c)
                    {
                        case '<': sb.Append("&lt;"); break;
                        case '>': sb.Append("&gt;"); break;
                        case '\"': sb.Append("&quot;"); break;
                        case '\'': sb.Append("&apos;"); break;
                        case '&': sb.Append("&amp;"); break;
                        default:
                            sb.Append(c);
                            break;
                    }
                }
            }
            return sb.ToString();
        }


        /// <summary>
        /// Whether a given character is allowed by XML 1.0.
        /// </summary>
        private static bool IsLegalXmlChar(int character)
        {
            return
            (
                 character == 0x9 /* == '\t' == 9   */          ||
                 character == 0xA /* == '\n' == 10  */          ||
                 character == 0xD /* == '\r' == 13  */          ||
                (character >= 0x20 && character <= 0xD7FF) ||
                (character >= 0xE000 && character <= 0xFFFD) ||
                (character >= 0x10000 && character <= 0x10FFFF)
            );
        }

        private static Regex m_xmlTag = new Regex(@"<script[^>]*>[\s\S]*?</script>|<[^>]*>|\s+|[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]",
       RegexOptions.IgnoreCase);
        /// <summary>
        /// 过滤html,invalid xml
        /// </summary>
        /// <returns></returns>
        public static string ToXmlText(this string html)
        {
            html = HttpUtility.HtmlDecode(html);
            html = m_xmlTag.Replace(html, string.Empty);
            return html.Trim();
        }
    }
}
